package socket

import (
	log "ac-common-go/glog"
	"ac-common-go/tls"
	"ac-julink/cache"
	"ac-julink/manager"
	"ac-julink/service"
	"ac-julink/tool/config"
	"ac-julink/tool/contains"
	"ac-julink/tool/data"
	"ac-julink/tool/util"
	"bufio"
	"container/list"
	"errors"
	"net"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

const (
	DATASEP byte = '\n'
)

type IdAllocator struct {
	idAcc      int32
	freeIDList list.List
	lock       *sync.Mutex
}

type BlackListConfig struct {
	FeatureCodeBlackList []string
	WifiIdBlackList      []string
	HbToPsMillThreshold  int64
	lock                 *sync.RWMutex
}

var BlackListConf = &BlackListConfig{
	FeatureCodeBlackList: make([]string, 0),
	WifiIdBlackList:      make([]string, 0),
	lock:                 new(sync.RWMutex),
}

func (this *BlackListConfig) SetConfigs(featureCodeBlackList, wifiIdBlackList string) {
	this.lock.Lock()
	defer this.lock.Unlock()
	this.FeatureCodeBlackList = strings.Split(featureCodeBlackList, ",")
	this.WifiIdBlackList = strings.Split(wifiIdBlackList, ",")
}

func (this *BlackListConfig) GetTwoList() ([]string, []string) {
	this.lock.RLock()
	defer this.lock.RUnlock()
	return this.FeatureCodeBlackList, this.WifiIdBlackList
}

func (this *BlackListConfig) GetFeatureCodeBlackList() []string {
	this.lock.RLock()
	defer this.lock.RUnlock()
	return this.FeatureCodeBlackList
}

func (this *BlackListConfig) GetWifiIdBlackList() []string {
	this.lock.RLock()
	defer this.lock.RUnlock()
	return this.WifiIdBlackList
}

var idAllocator = &IdAllocator{lock: new(sync.Mutex)}

type SocketSession struct {
	//socket连接会话信息
	OnClose func()

	//conn *net.TCPConn
	conn net.Conn

	wifiId string

	connTime string

	sendList chan string

	clientIp string

	isClosed bool

	sessionId int
	isTls     bool

	// default 0 AESECB
	// 1 AES256CBC
	CryptType int

	Time101 int64
}

func (self *IdAllocator) allocateId() int {
	self.lock.Lock()
	defer self.lock.Unlock()
	var newId int
	if self.freeIDList.Len() > 0 {
		freeEle := self.freeIDList.Front()
		self.freeIDList.Remove(freeEle)
		var ok bool
		newId, ok = freeEle.Value.(int)
		//xwj add begin 2019/1/17
		if !ok {
			log.Errorf("It's not ok for type int,freeEle.Value[%+v]", freeEle.Value)
			self.lock.Unlock()
			return -1
		}
		//xwj add end 2019/1/17
	} else {
		accId := atomic.AddInt32(&self.idAcc, 1)
		newId = int(accId)
	}
	return newId
}

func (self *IdAllocator) freeId(id int) {
	self.lock.Lock()
	self.freeIDList.PushBack(id)
	self.lock.Unlock()
}

func newSession(conn net.Conn, connTime string, isTls bool) (*SocketSession, error) {
	if !isTls {
		tcpConn, ok := conn.(*net.TCPConn)
		if !ok {
			return nil, errors.New("conn tcp type is invalid")
		}
		_, err := ApplyTcp(tcpConn)
		if err != nil {
			return nil, err
		}

		self := &SocketSession{
			conn:      conn,
			connTime:  connTime,
			sendList:  make(chan string, 64),
			clientIp:  getIp(tcpConn),
			sessionId: idAllocator.allocateId(),
			isTls:     isTls,
			Time101:   time.Now().UnixNano() / int64(time.Millisecond),
		}
		return self, nil

	} else {
		tlsConn, ok := conn.(*tls.Conn)
		if !ok {
			return nil, errors.New("tls.Conn type is invalid")
		}
		tcpConn, ok := tlsConn.GetConn().(*net.TCPConn)
		if !ok {
			return nil, errors.New("net.TCPConn type is invalid")
		}
		_, err := ApplyTcp(tcpConn)
		if err != nil {
			return nil, err
		}
		self := &SocketSession{
			conn:      conn,
			connTime:  connTime,
			sendList:  make(chan string, 64),
			clientIp:  getIp(tcpConn),
			sessionId: idAllocator.allocateId(),
			isTls:     isTls,
			Time101:   time.Now().UnixNano() / int64(time.Millisecond),
		}
		return self, nil

	}
}

func getIp(conn *net.TCPConn) string {
	defer func() {
		if pe := recover(); pe != nil {
			log.Errorf("get ip panic:%+v\n%s", pe, util.PanicTrace())
		}
	}()
	//xwj add begin 2019/1/17
	addr, ok := conn.RemoteAddr().(*net.TCPAddr)
	if !ok {
		log.Error("It's not ok for type *net.TCPAddr,conn.RemoteAddr()]")
		return ""
	}
	//xwj add end 2019/1/17
	return addr.IP.String()
}

// xiaohong add
func getTlsIp(conn *tls.Conn) string {
	defer func() {
		if pe := recover(); pe != nil {
			log.Errorf("get ip panic:%+v\n%s", pe, util.PanicTrace())
		}
	}()

	addr, ok := conn.RemoteAddr().(*net.TCPAddr)
	if !ok {
		log.Error("It's not ok for type *net.TCPAddr,conn.RemoteAddr()]")
		return ""
	}
	return addr.IP.String()
}

func (self *SocketSession) WifiID() string {
	return self.wifiId
}

func (self *SocketSession) SessionId() int {
	return self.sessionId
}

func (self *SocketSession) ClientIp() string {
	return self.clientIp
}

// 数据接收
func (self *SocketSession) recvThread(context *cache.SyncContext, client *manager.AcRestClient) {
	defer func() {
		if pe := recover(); pe != nil {
			log.Errorf("recv thread panic:%+v\n%s", pe, util.PanicTrace())
			self.Close(context, client)
		}
	}()
	log.Info("recvThread======>>>>")
	br := bufio.NewReader(self.conn)
	var response string
	for {
		if self.isClosed {
			log.Infof("session is closed, recv thread break: %s", self.wifiId)
			break
		}
		log.Infof("start to read msg:%s", self.wifiId)
		inputData, err := br.ReadString(DATASEP)
		log.Infof("end to read msg:%s,%s", self.wifiId, inputData)
		if err != nil {
			log.Errorf("receive data error: wifiid: %s, sessionId: %s, error: %+v", self.wifiId, self.sessionId, err)
			goto onClose
		}
		log.Infof("receive new data: %s, %s", self.wifiId, inputData)
		//黑名单校验
		if !self.SafetyCheck() {
			log.Errorf("safety check failed!!!")
			goto onClose
		}

		sess := SocketManager.SessionMgr.GetSession(self.SessionId())
		response, err = service.DispatchMessage(inputData, self.wifiId, self.clientIp, self.sessionId, context, client, sess.CryptType, sess.Time101, sess.SessionId())

		if err != nil && strings.Compare(err.Error(), "CLOSESESSION") == 0 {
			log.Errorf("DispatchMessage error: %+v", err)
			//TODO:这个地方是否要关闭连接？业务性的错误应该不需要关闭
			goto onClose
		} else {
			//TODO:err不是nil的情况，service要返回给client的response
			if response != "" {
				timeStart := time.Now().UnixNano()
				log.Infof("start to write msg:%s to:%s", response, self.wifiId)
				self.sendList <- response
				log.Infof("end to write msg:%s to:%s,consume:%dms", response, self.wifiId, (time.Now().UnixNano()-timeStart)/1e6)
			} else {
				log.Infof("response is empty no need to write:%s", self.wifiId)
			}
		}
	}

onClose:
	self.Close(context, client)
}

// 数据发送线程
func (self *SocketSession) sendThread(context *cache.SyncContext, client *manager.AcRestClient) {
	defer func() {
		if pe := recover(); pe != nil {
			log.Errorf("send thread panic:%+v\n%s", pe, util.PanicTrace())
			self.Close(context, client)
		}
	}()
	log.Info("sendThread======>>>>")
	var packagemsg string
	wr := bufio.NewWriter(self.conn)
	for {
		log.Infof("start to send to:%s", self.wifiId)
		packagemsg = <-self.sendList
		if self.isClosed {
			log.Infof("session is closed, send thread break: %s", self.wifiId)
			break
		}
		if data.WriteTimeout > 0 {
			self.conn.SetWriteDeadline(time.Now().Add(time.Duration(data.WriteTimeout) * time.Millisecond))
		}
		log.Infof("sendThread packagemsg:[%s]", packagemsg)
		_, err := wr.WriteString(packagemsg)
		if err != nil {
			log.Errorf("write data error: %s, %s, %+v", self.wifiId, self.conn.RemoteAddr(), err)
			goto exitsendloop
		}
		err = wr.Flush()
		if err != nil {
			log.Errorf("send packageMsg error:  %s, %s, %+v", self.wifiId, self.conn.RemoteAddr(), err)
			self.Close(context, client)
		}
		log.Infof("end to send to:%s", self.wifiId)
	}

exitsendloop:
	self.Close(context, client)
}

func (self *SocketSession) SendMessage(message string) error {
	if self.isClosed {
		return errors.New("socket session is closed")
	}
	select {
	case self.sendList <- message:
		log.Infof("send message success:%s", self.wifiId)
	case <-time.After(time.Duration(data.SendMsgTimeout) * time.Millisecond):
		log.Errorf("send message timeout:%s", self.wifiId)
		return errors.New("send msg timeout!")
	}
	return nil
}

func (self *SocketSession) run(context *cache.SyncContext, client *manager.AcRestClient) {

	// 接收线程
	go self.recvThread(context, client)

	// 发送线程
	go self.sendThread(context, client)
}

func (self *SocketSession) AcceptConn(context *cache.SyncContext, client *manager.AcRestClient) (_wifiid string, _cryptType int, err error) {
	var cryptType int
	defer func() {
		if pe := recover(); pe != nil {
			log.Errorf("accept connection panic:%+v\n%s", pe, util.PanicTrace())
			self.Close(context, client)
			err = errors.New("accept connection panic")
		}
	}()
	br := bufio.NewReader(self.conn)
	inputData, _err := br.ReadString(DATASEP)
	if _err != nil {
		log.Errorf("accept connection data error:  %+v", _err)
		self.Close(context, client)
		return "", cryptType, _err
	}
	if inputData == "" {
		log.Error("ReadString data is NULL.")
		self.Close(context, client)
		return "", cryptType, _err
	}
	log.Infof("Recv data:  %s", inputData)
	if len(inputData) >= data.MUX_MSG_LEN || len(inputData) < 7 {
		log.Errorf("Read data is too long or too short. %s", inputData)
		self.Close(context, client)
		return "", cryptType, _err
	}
	//数据格式,长度并解析内容
	//1、数据解析,只解析101认证消息的wifiid
	var _msgResult string
	timeStart := time.Now().UnixNano()
	_wifiid, _msgResult, cryptType, err = service.HandelSessionData(inputData, self.clientIp, self.sessionId, context, client)

	featureCodeBlackList, wifiIdBlackList := BlackListConf.GetTwoList()
	// black list
	if contains.StrContainsElement(featureCodeBlackList, _wifiid) || contains.Contains(wifiIdBlackList, _wifiid) {
		log.Errorf("RECORD:%d,%s,%dms,err:%s", 101, _wifiid, (time.Now().UnixNano()-timeStart)/1e6, "this wifiId is in blacklist, close this session now.")
		self.Close(context, client)
	}

	log.Warningf("RECORD:%d,%s,%dms,err:%+v", 101, _wifiid, (time.Now().UnixNano()-timeStart)/1e6, err)
	if err != nil {
		log.Errorf("HandelSessionData error.wifiId:err:%+v", err)
		self.Close(context, client)
		return _wifiid, cryptType, err
	} else { //101设备认证成功,返回token信息
		self.wifiId = _wifiid
		if !self.SafetyCheck() {
			log.Error("safety check failed!!!")
			self.Close(context, client)
			return _wifiid, cryptType, errors.New("safety check failed!!!")
		}
		wr := bufio.NewWriter(self.conn)
		_, err := wr.WriteString(_msgResult)
		if err != nil {
			log.Errorf("send data error: %s, %+v", self.wifiId, err)
			self.Close(context, client)
		} else {
			err = wr.Flush()
			if err != nil {
				log.Errorf("send result is error: %s, %+v", self.wifiId, err)
				self.Close(context, client)
			}
		}
	}
	return _wifiid, cryptType, nil
}

func (self *SocketSession) Close(context *cache.SyncContext, client *manager.AcRestClient) {
	self.conn.Close()
	if !self.isClosed {
		self.isClosed = true
		close(self.sendList)
		if self.wifiId != "" {
			service.HandelDeviceOffline(self.wifiId, self.clientIp, self.sessionId, context, client, self.CryptType)
			if self.OnClose != nil {
				idAllocator.freeId(self.sessionId)
				self.OnClose()
			}
		}
	}
}

func (self *SocketSession) SafetyCheck() bool {
	if self.wifiId == "" || self.clientIp == "" {
		log.Errorf("session's wifiid or ip is empty:%s", self.wifiId)
		return false
	}
	wifiIdList := config.GetConfigValue("WGNS", "wifi_black_list")
	ipList := config.GetConfigValue("WGNS", "ip_black_list")
	if wifiIdList != "" && strings.Contains(wifiIdList, self.wifiId) {
		log.Errorf("wifiid:%s is in the black list, reject it ", self.wifiId)
		return false
	}
	if ipList != "" && strings.Contains(ipList, self.clientIp) {
		log.Errorf("ip is in the black list, reject it %s", self.wifiId)
		return false
	}
	return true
}
